package org.idea.qiyu.framework.rpc.dubbo.router.starter.cluster;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
import org.apache.dubbo.rpc.protocol.InvokerWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 按照zone来选择不同调路由决策
 *
 * @Author linhao
 * @Date created in 4:45 下午 2021/8/1
 */
public class ZoneSelectRouter extends AbstractRouter {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZoneSelectRouter.class);

    @Override
    public URL getUrl() {
        return null;
    }

    @Override
    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        String consumerZone = System.getProperty("zone");
        //如果没有带zone不影响路由筛选
        if (StringUtils.isEmpty(consumerZone)) {
            return invokers;
        }
        LOGGER.info("consumerZone is " + consumerZone);
        List<QiyuInvokerDelegate<T>> hackedInvokers = invokers.stream().map(QiyuInvokerDelegate::new).collect(Collectors.toList());
        List<Invoker<T>> invokerResult = new LinkedList<>();
        for (QiyuInvokerDelegate<T> hackedInvoker : hackedInvokers) {
            URL providerUrl = hackedInvoker.getProviderUrl();
            //默认的invoker没有zone
            String providerZone = providerUrl.getParameter("zone");
            if (StringUtils.isEmpty(providerZone)) {
                continue;
            } else if (providerZone.equals(consumerZone)) {
                invokerResult.add(hackedInvoker);
                LOGGER.info("[ZoneSelectRouter] 匹配目标zone,consumerZone: " + consumerZone + ";providerZone: " + providerZone);
            }
        }
        if(CollectionUtils.isEmpty(invokerResult)){
            LOGGER.info("[ZoneSelectRouter] 没有匹配的zone，invokerResult is empty,不走zone的路由过滤");
            return invokers;
        }
        return invokerResult;
    }

    /**
     * 默认的Invoker实现是不可见的com.alibaba.dubbo.registry.integration.RegistryDirectory.InvokerDelegate
     * 这里添加的获取ProviderUrl是为了做路由算法规则
     *
     * @param <T>
     */
    private static class QiyuInvokerDelegate<T> extends InvokerWrapper<T> {
        private URL providerUrl;

        public QiyuInvokerDelegate(Invoker<T> invoker) {
            super(invoker, invoker.getUrl());
            providerUrl = hackUrl(invoker);
        }

        public URL getProviderUrl() {
            return providerUrl;
        }

        private <E> URL hackUrl(Invoker<T> invoker) {
            try {
                Field providerUrlField = invoker.getClass().getDeclaredField("providerUrl");
                providerUrlField.setAccessible(true);
                return (URL) providerUrlField.get(invoker);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new IllegalStateException("Could not get providerUrl", e);
            }
        }
    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean isForce() {
        return false;
    }

    @Override
    public int getPriority() {
        return -100;
    }
}
